/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef TRI_XML_H_
#define TRI_XML_H_

#include <iostream>
#include <string>
#include <vector>
#include <map>

#include "tinyxml/tinyxml.h"
#include "utils.h"
#include "exception.h"

namespace tri{

/**
 * Xml node value object 
 */
class xml_value{
public:
    xml_value(std::string v = "0");
    virtual ~xml_value();
    
    /**
     * Template function to retrieve the contained value 
     * in the templated type. Supported types are std::string,
     * int and float
     */
    template<class T>
    inline T getValue(){
        T val;
        _getValue(val);
        return val;
    }
    
    /**
     * Set the instance value
     */
    void setValue(std::string v);
    
protected:
    
    void _getValue(int&);
    void _getValue(float&);
    void _getValue(std::string &);
    
private:
    std::string value;
    std::string std_value;
    
};

class xml_node;

/**
 * Node management class 
 */
class xml_node_tree{
    
private:
    std::map< std::string, std::vector<xml_node> > elem;
    static void loadElement(TiXmlNode* pParent,xml_node* _node);
    
    
public:
    xml_node_tree();
    virtual ~xml_node_tree();
    
    /**
     * Returns true if this tree contains 
     * elements of the given type.
     */
    bool hasElement(std::string type);
    
    /**
     * Returns the number of  
     * elements of the given type.
     */
    int countElement(std::string type);
    
    /**
     * Get the reference to a child element 
     * elements of the given type.
     */
    xml_node* getElement(std::string type,int index = 0);
    
    /**
     * Add a new element to the tree
     * 
     * @param type : Element type
     * @param value : [optional] value
     */
    void addElement(std::string type,char* value="0");
    
    static void loadXMLFromFile(const char* pFilename,xml_node* doc);
    
    /**
     * Delete all data in tree
     */
    void clear();
    
};
/**
 * Xml Node
 */
class xml_node : public xml_value,public xml_node_tree{    
private:
    std::map< std::string, std::vector<xml_value> > attr;
    std::string type;
public:
    xml_node(char* value="0");
    virtual ~xml_node();
    
    /**
     * Returns true if this node contains 
     * an attribute of the given type.
     */
    bool hasAttribute(std::string type);
    
    /**
     * Returns the number of  
     * attributes of the given type.
     */
    int countAttribute(std::string type);
    
    /**
     * Template function to retrieve the attribute value 
     * in the templated type. Supported types are std::string,
     * int and float
     */
    template<class T>
    inline T getAttribute(std::string _attr,int index = 0){
        if(!hasAttribute(_attr)){ 
            throw TriXMLException("XML Attribute does not exists!!!\n");
        }
        if( index < 0){
            throw TriXMLException("Index cannot be less than 0!!!\n");
        }
        if( attr.count(_attr) <= index){
            throw TriXMLException("Index out of range!!!\n");
        }
        
        return attr[_attr][index].getValue<T>();
    }
    /**
     * Add a new attribute to the node.
     * 
     * @param type : Attribute type
     * @param value : [optional] value
     */
    void addAttribute(std::string type,std::string value);

};


/**
 * XML parser wrapper
 */
class xml_doc :public xml_node{
public:
    xml_doc();
    virtual ~xml_doc();
    
    /**
     * Load xml data from file
     * 
     * @param File : Xml file on file system  
     */
    void load(const char* File);
};

}

#endif
